﻿using CLM.Gestao.Administrador.DAL;
using CLM.Gestao.Importacao;
using CLM.Gestao.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;

namespace CLM.Gestao.Administrador.BLL
{
    public class CentroCusto : CentroCustoBLO<TB_CENTRO_CUSTO>
    {
        

        #region Propriedades
        private class Rateio { public static int IdPeriodoAno { get; set; } }
        #endregion Propriedades
        #region Conversões
        public CentroCusto() { }
        public CentroCusto(int anoPeriodo)
        {
            PeriodoAno periodoAno = new PeriodoAno();
            Rateio.IdPeriodoAno = (from ano in periodoAno.GetLista(new PeriodoAno.Lista(){ AnoPeriodo = anoPeriodo.ToString() }) select ano.IdPeriodoAno).FirstOrDefault();
        }
        /// <summary>
        /// Converte uma lista de objetos do tipo Lista para uma lista de objetos do tipo correspondente no EDM
        /// </summary>
        /// <param name="pLista"></Lista></param>
        /// <returns></returns>
        private List<TB_CENTRO_CUSTO> ConvertToEDM(List<Lista> pLista)
        {
            List<TB_CENTRO_CUSTO> List = (from l in pLista
                                          select new TB_CENTRO_CUSTO
                                                {
                                                    ID_CENTRO_CUSTO = l.IdCentroCusto,
                                                    CODIGO_CENTRO_CUSTO = l.CodigoCentroCusto.Trim(),
                                                    ID_CATEGORIA_CENTRO_CUSTO = l.IdCategoriaCentroCusto,
                                                    INDICA_ALOCACAO = l.IndicaAlocacao,
                                                    INDICA_CENTRO_CUSTO_PROJETO = l.IndicaCentroCustoProjeto,
                                                    INDICA_DESPESAS_CORPORATIVAS = l.IndicaDespesaCorporativa,
                                                    INDICA_GERENCIAL = l.IndicaGerencial,
                                                    INDICA_TRADE_ALLOWANCE = l.IndicaTradeAllowance,
                                                    NOME_CENTRO_CUSTO = l.NomeCentroCusto.Trim(),
                                                    SIGLA_CENTRO_CUSTO = l.SiglaCentroCusto
                                                }).ToList();
            return List;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pContext"></param>
        /// <returns></returns>
        /// 

        private List<Lista> ConvertToContext(IQueryable<TB_CENTRO_CUSTO> pContext)
        {
            try
            {
                List<Lista> List = (from l in pContext
                                    select new Lista
                                    {
                                        CodigoCentroCusto = l.CODIGO_CENTRO_CUSTO.Trim(),
                                        IdCentroCusto = l.ID_CENTRO_CUSTO,
                                        IdCategoriaCentroCusto = l.TB_CATEGORIA_CENTRO_CUSTO.ID_CATEGORIA_CENTRO_CUSTO,
                                        IdDriverRateioTipo = l.TB_CENTRO_CUSTO_DRIVER_RATEIO_TIPO.Where(x => x.ID_PERIODO_ANO.Equals(Rateio.IdPeriodoAno) && x.TB_CENTRO_CUSTO.ID_CENTRO_CUSTO.Equals(l.ID_CENTRO_CUSTO)).Select(x => x.ID_DRIVER_RATEIO_TIPO).FirstOrDefault(),
                                        IdIndicadorDespesa = l.TB_CATEGORIA_CENTRO_CUSTO.ID_INDICADOR_DESPESA,
                                        IndicaAlocacao = l.INDICA_ALOCACAO,
                                        IndicaCentroCustoProjeto = l.INDICA_CENTRO_CUSTO_PROJETO,
                                        IndicaDespesaCorporativa = l.INDICA_DESPESAS_CORPORATIVAS,
                                        IndicaGerencial = l.INDICA_GERENCIAL,
                                        IndicaTradeAllowance = l.INDICA_TRADE_ALLOWANCE,
                                        IndicaFamiliaProduto = l.TB_CENTRO_CUSTO_PRODUTO_ATIVO.Where(w => w.INDICA_ATIVO == true).Select(x => x.INDICA_FAMILIA_PRODUTO).FirstOrDefault(),
                                        IndicaTodosProdutos = l.TB_CENTRO_CUSTO_PRODUTO_ATIVO.Where(w => w.INDICA_ATIVO == true).Select(x => x.INDICA_TODOS_PRODUTOS).FirstOrDefault(),
                                        IndicaUnidadeNegocio = l.TB_CENTRO_CUSTO_PRODUTO_ATIVO.Where(w => w.INDICA_ATIVO == true).Select(x => x.INDICA_UNIDADE_NEGOCIO).FirstOrDefault(),
                                        NomeCategoriaCentroCusto = l.TB_CATEGORIA_CENTRO_CUSTO.NOME_CATEGORIA_CENTRO_CUSTO.Trim(),
                                        NomeCentroCusto = l.NOME_CENTRO_CUSTO.Trim(),
                                        SiglaCentroCusto = l.SIGLA_CENTRO_CUSTO.Trim()
                                    }).ToList();

                return List;
            }
            catch (Exception) { throw; }
        }

        #endregion Conversões

        #region CRUD

        public void Add(List<Lista> pLista)
        {
            try
            {
                foreach (var item in ConvertToEDM(pLista))
                    base.Add(item);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public void Delete(List<Lista> pLista)
        {
            try
            {
                foreach (var item in ConvertToEDM(pLista))
                    base.Delete(item);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public void Update(List<Lista> pLista)
        {
            try
            {
                foreach (var item in ConvertToEDM(pLista))
                    base.Update(item);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public void IndicaTradeAllowance()
        {
            try
            {



            }
            catch (Exception) { throw; }
        }


        /// <summary>
        /// Efetua a pesquisa na tabela, filtrando as solicitações do usuário
        /// </summary>
        /// <param name="pPesquisa">pPesquisa</param>
        /// <returns>Lista</returns>
        private IQueryable<TB_CENTRO_CUSTO> Get(Lista pPesquisa)
        {
            IQueryable<TB_CENTRO_CUSTO> Consulta;

            Consulta = from p in GetAll()
                       select p;

            if (pPesquisa != null)
            {
                if (!string.IsNullOrWhiteSpace(pPesquisa.CodigoCentroCusto))
                    Consulta = from p in Consulta where p.CODIGO_CENTRO_CUSTO.Contains(pPesquisa.CodigoCentroCusto) select p;

                if (!string.IsNullOrWhiteSpace(pPesquisa.NomeCentroCusto))
                    Consulta = from p in Consulta where p.NOME_CENTRO_CUSTO.Contains(pPesquisa.NomeCentroCusto) select p;

                if (pPesquisa.IdCategoriaCentroCusto != null)
                    Consulta = from p in Consulta where p.ID_CATEGORIA_CENTRO_CUSTO == pPesquisa.IdCategoriaCentroCusto select p;

                if (pPesquisa.IndicaTradeAllowance != null)
                    Consulta = from p in Consulta where p.INDICA_TRADE_ALLOWANCE == pPesquisa.IndicaTradeAllowance select p;

                if (pPesquisa.IndicaGerencial != null)
                    Consulta = from p in Consulta where p.INDICA_GERENCIAL == pPesquisa.IndicaGerencial select p;

                if (pPesquisa.IndicaIndicadorDespesa != null)
                    Consulta = from p in Consulta where p.TB_CATEGORIA_CENTRO_CUSTO.ID_INDICADOR_DESPESA != null select p;


            }
            return Consulta;
        }

        #endregion CRUD

        #region Retorna Listas e Itens

        public List<Lista> GetLista()
        {
            return GetLista(new Lista());
        }

        public List<Lista> GetLista(Lista pPesquisa)
        {
            var ListaCentroCusto = ConvertToContext(Get(pPesquisa));
            return ListaCentroCusto;
        }

        public List<Itens> GetItens()
        {
            return GetItens(new Lista());
        }

        public List<Itens> GetItens(Lista pPesquisa)
        {
            List<Itens> ListaCentroCusto = (from p in Get(pPesquisa)
                                            select new Itens()
                                            {
                                                IdCentroCusto = p.ID_CENTRO_CUSTO,
                                                CodigoCentroCusto = p.CODIGO_CENTRO_CUSTO,
                                                NomeCentroCusto = p.NOME_CENTRO_CUSTO
                                            }).ToList();
            return ListaCentroCusto;
        }

        #endregion Retorna Listas e Itens

        #region Consultas

        public List<Produto.Lista> GetProdutos(int pIdCentroCusto, int pIdPeriodo)
        { 

            Produto produto = new Produto();

            Lista centro = (from s in GetLista()
                            where s.IdCentroCusto == pIdCentroCusto
                            select new Lista
                            {
                                IdCentroCusto = s.IdCentroCusto,
                                IndicaUnidadeNegocio = s.IndicaUnidadeNegocio,
                                IndicaTodosProdutos = s.IndicaTodosProdutos,
                                IndicaFamiliaProduto = s.IndicaFamiliaProduto
                            }).ToList().First();

            CentroCustoProdutoAtivo CentroCustoProduto = new CentroCustoProdutoAtivo();

            if (centro.IndicaTodosProdutos == true)
                return (from s in produto.GetLista(new Produto.Lista() { IdPeriodo = pIdPeriodo})
                        select new Produto.Lista
                        {
                            IdProduto = s.IdProduto,
                            IdProdutoPai = s.IdProdutoPai,
                            IdProdutoFamilia = s.IdProdutoFamilia,
                            DriverGGF = s.DriverGGF,
                            DriverVenda = s.DriverVenda,
                            DriverNetNet = s.DriverNetNet,
                            DriverGradePromocional = s.DriverGradePromocional
                        }).ToList();
            else if (centro.IndicaUnidadeNegocio == true)
            {
                centro.UnidadesNegocio = CentroCustoProduto.GetLista(new CentroCustoProdutoAtivo.Lista() { IdCentroCusto = centro.IdCentroCusto, IndicaAtivo = true });
                return (from s in produto.GetLista(new Produto.Lista() { IdPeriodo = pIdPeriodo })
                        join u in centro.UnidadesNegocio
                        on s.IdUnidadeNegocio equals u.IdUnidadeNegocio
                        select new Produto.Lista
                        {
                            IdProduto = s.IdProduto,
                            IdProdutoPai = s.IdProdutoPai,
                            IdProdutoFamilia = s.IdProdutoFamilia,
                            DriverGGF = s.DriverGGF,
                            DriverVenda = s.DriverVenda,
                            DriverNetNet = s.DriverNetNet,
                            DriverGradePromocional = s.DriverGradePromocional
                        }).ToList();
            }
            else if (centro.IndicaFamiliaProduto == true)
            {
                centro.ProdutosFamilias = CentroCustoProduto.GetLista(new CentroCustoProdutoAtivo.Lista() { IdCentroCusto = centro.IdCentroCusto, IndicaAtivo = true });
                return (from s in produto.GetLista(new Produto.Lista() { IdPeriodo = pIdPeriodo })
                        join f in centro.ProdutosFamilias
                        on s.IdProdutoFamilia equals f.idProdutoFamilia
                        select new Produto.Lista
                        {
                            IdProduto = s.IdProduto,
                            IdProdutoPai = s.IdProdutoPai,
                            IdProdutoFamilia = s.IdProdutoFamilia,
                            DriverGGF = s.DriverGGF,
                            DriverVenda = s.DriverVenda,
                            DriverNetNet = s.DriverNetNet,
                            DriverGradePromocional = s.DriverGradePromocional
                        }).ToList();
            }
            else
                return centro.Produtos;
        }

        #endregion Consultas

        #region Processos

        #region Sincroniza Centros de Custo

        public void SincronizaCentrosCusto()
        {
            try
            {
                OnImportData(this, new ImportDataEventArgs("Acessando Banco de Dados Protheus..."));

                ContaContabil ContaContabil = new ContaContabil();

                Protheus.BLL.SpCentroCustoGaldermaNacional ProcedureCentrosCusto = new Protheus.BLL.SpCentroCustoGaldermaNacional();
                List<Lista> CentrosProtheus = (from p in ProcedureCentrosCusto.ExecutaProcedure()
                                               select new Lista
                                               {
                                                   NomeCentroCusto = p.NomeCentroCusto,
                                                   CodigoCentroCusto = p.CodigoCentroCusto
                                               }).ToList();

                OnImportData(this, new ImportDataEventArgs("Acessando informações de Centros de Custos..."));

                List<Lista> CentrosSQL = (from p in Get(new Lista())
                                          select new Lista
                                          {
                                              CodigoCentroCusto = p.CODIGO_CENTRO_CUSTO,
                                              NomeCentroCusto = p.NOME_CENTRO_CUSTO,
                                              IdCentroCusto = p.ID_CENTRO_CUSTO,
                                              IndicaGerencial = p.INDICA_GERENCIAL
                                          }).ToList();

                var Novos = (from pr in CentrosProtheus
                             join db in CentrosSQL
                             on pr.CodigoCentroCusto equals db.CodigoCentroCusto
                             into centros
                             from db in centros.DefaultIfEmpty()
                             where db == null
                             select new Lista
                             {
                                 CodigoCentroCusto = pr.CodigoCentroCusto,
                                 NomeCentroCusto = pr.NomeCentroCusto
                             }).ToList();

                int i = 0;
                if (Novos != null && Novos.Count() > 0)
                {
                    foreach (var item in Novos)
                    {
                        this.Add(new TB_CENTRO_CUSTO() { CODIGO_CENTRO_CUSTO = item.CodigoCentroCusto, NOME_CENTRO_CUSTO = item.NomeCentroCusto, INDICA_GERENCIAL = false });
                        ContaContabil.Add(new TB_CONTA_CONTABIL() { CODIGO_CONTA_CONTABIL = item.CodigoCentroCusto, DESCRICAO_CONTA_CONTABIL = item.NomeCentroCusto, INDICA_GERENCIAL = true });
                        i++;
                        OnImportData(this, new ImportDataEventArgs("Incluindo Centros de Custos...", i, Novos.Count()));
                    }
                }

                List<Lista> Alterados = (from pr in CentrosProtheus
                                         join db in CentrosSQL
                                         on pr.CodigoCentroCusto equals db.CodigoCentroCusto
                                         where pr.NomeCentroCusto != db.NomeCentroCusto
                                         select new Lista
                                         {
                                             IdCentroCusto = db.IdCentroCusto,
                                             CodigoCentroCusto = pr.CodigoCentroCusto,
                                             NomeCentroCusto = pr.NomeCentroCusto,
                                             IndicaGerencial = db.IndicaGerencial
                                         }).ToList();
                i = 0;
                if (Alterados != null && Alterados.Count() > 0)
                {
                    foreach (var item in Alterados)
                    {
                        this.Update(new TB_CENTRO_CUSTO() { ID_CENTRO_CUSTO = item.IdCentroCusto, CODIGO_CENTRO_CUSTO = item.CodigoCentroCusto, NOME_CENTRO_CUSTO = item.NomeCentroCusto, INDICA_GERENCIAL = item.IndicaGerencial });
                        ContaContabil.Update(new TB_CONTA_CONTABIL() { ID_CONTA_CONTABIL = item.IdCentroCusto, CODIGO_CONTA_CONTABIL = item.CodigoCentroCusto, DESCRICAO_CONTA_CONTABIL = item.NomeCentroCusto, INDICA_GERENCIAL = item.IndicaGerencial });
                        i++;
                        OnImportData(this, new ImportDataEventArgs("Alterando Centros de Custos...", i, Alterados.Count()));
                    }
                }

                if (Novos != null && Novos.Count() == 0 && Alterados != null && Alterados.Count() == 0)
                    OnImportData(this, new ImportDataEventArgs("Os Centros de Custos já estão sincronizados. Não foram realizadas alterações."));
                else
                    OnImportData(this, new ImportDataEventArgs("Sincronização efetuada com sucesso."));
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        #endregion Sincroniza Centros de Custo

        #endregion

        #region Classes

        /// <summary>
        /// Classe utilizada para preenchimento de Combos
        /// </summary>
        public class Itens
        {
            public int IdCentroCusto { get; set; }
            public string CodigoCentroCusto { get; set; }
            public string NomeCentroCusto { get; set; }
        }

        /// <summary>
        /// Classe utilizada para preenchimento de Grids
        /// </summary>
        /// 

        
        public class Lista
        {
            public Lista()
            {
                Produtos = new List<Produto.Lista>();
            }

            public int IdCentroCusto { get; set; }
            public string CodigoCentroCusto { get; set; }
            public string SiglaCentroCusto { get; set; }
            public string NomeCentroCusto { get; set; }
            public bool? IndicaDespesaCorporativa { get; set; }
            public bool? IndicaCentroCustoProjeto { get; set; }
            public int? IdCategoriaCentroCusto { get; set; }
            public string NomeCategoriaCentroCusto { get; set; }
            public bool? IndicaTradeAllowance { get; set; }
            public bool? IndicaGerencial { get; set; }
            public bool? IndicaAlocacao { get; set; }
            public bool? IndicaTodosProdutos { get; set; }
            public bool? IndicaFamiliaProduto { get; set; }
            public bool? IndicaUnidadeNegocio { get; set; }
            public List<Produto.Lista> Produtos { get; set; }
            public List<CentroCustoProdutoAtivo.Lista> ProdutosFamilias { get; set; }
            public List<CentroCustoProdutoAtivo.Lista> UnidadesNegocio { get; set; }
            public int IdDriverRateioTipo { get; set; }
            public int? IdIndicadorDespesa { get; set; }
            public bool? IndicaIndicadorDespesa { get; set; }
        }

        #endregion Classes

        public event ImportDataEventHandler OnImportData;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CentroCustoBLO<T> : IBLO<T> where T : TB_CENTRO_CUSTO
    {
        /// <summary>
        /// 
        /// </summary>
        private CentroCustoDAO<T> _base = new CentroCustoDAO<T>();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pEntity"></param>
        /// <returns></returns>
        public T Add(T pEntity)
        {
            Validate(pEntity);
            _base.Add(pEntity);
            return _base.Save(pEntity);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pEntity"></param>
        /// <returns></returns>
        public T Save(T pEntity)
        {
            Validate(pEntity);
            return _base.Save(pEntity);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pEntity"></param>
        public void Delete(T pEntity)
        {
            _base.Delete(pEntity);
            _base.Save(pEntity);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pEntity"></param>
        /// <returns></returns>
        public T Update(T pEntity)
        {
            Validate(pEntity);
            _base.Update(pEntity);
            return _base.Save(pEntity);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IQueryable<T> GetAll()
        {
            return _base.GetAll();
        }

        public void Validate(T pEntity)
        {
            int validate = (from u in _base.GetAll()
                            where (u.CODIGO_CENTRO_CUSTO == pEntity.CODIGO_CENTRO_CUSTO && u.ID_CENTRO_CUSTO != pEntity.ID_CENTRO_CUSTO)
                            select u).Count();

            if (validate > 0)
                throw new Exception("Já existe um centro de custo cadastrado com esse código.");

            validate = (from u in _base.GetAll()
                        where ((u.NOME_CENTRO_CUSTO == pEntity.NOME_CENTRO_CUSTO && u.CODIGO_CENTRO_CUSTO == pEntity.CODIGO_CENTRO_CUSTO) && u.ID_CENTRO_CUSTO != pEntity.ID_CENTRO_CUSTO)
                        select u).Count();

            if (validate > 0)
                throw new Exception("Já existe um centro de custo cadastrado com esse nome.");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pEntity"></param>
        public void ValidateDelete(T pEntity)
        {
            //int Perfilvalidate = (from u in _base.GetAll()
            //                      where u.TB_USUARIO.Where(w => w.ID_PERFIL_ACESSO == pEntity.ID_PERFIL_ACESSO).Select(s => s).Count() > 0
            //                      select u).Count();

            //if (Perfilvalidate > 0)
            //    throw new Exception("Existem usuários relacionados com este perfil de acesso.\nA operação não pôde ser concluída");
        }
    }
}